﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace ETModel
{
	[ObjectSystem]
	public class SessionAwakeSystem : AwakeSystem<Session, AChannel>
	{
		public override void Awake(Session self, AChannel b)
		{
			self.Awake(b);
		}
	}

	public sealed class Session : Entity
	{
		private static byte RpcId { get; set; }
		private AChannel channel;

		private readonly Dictionary<byte, Action<IInMessage>> requestCallback = new Dictionary<byte, Action<IInMessage>>();

		public NetworkComponent Network
		{
			get
			{
				return this.GetParent<NetworkComponent>();
			}
		}

		public int Error
		{
			get
			{
				return this.channel.Error;
			}
			set
			{
				this.channel.Error = value;
			}
		}

		public void Awake(AChannel aChannel)
		{
			this.channel = aChannel;
			this.requestCallback.Clear();
			long id = this.Id;
			channel.ErrorCallback += (c, e) =>
			{
                Log.Error($"ErrorId={e}");
				this.Network.Remove(id); 
			};
			channel.ReadCallback += this.OnRead;
		}
		
		public override void Dispose()
		{
			if (this.IsDisposed)
			{
				return;
			}

			this.Network.Remove(this.Id);

			base.Dispose();
			
			foreach (Action<IInMessage> action in this.requestCallback.Values.ToArray())
			{
				action.Invoke(null);
			}
			
			this.channel.Dispose();
			
			this.requestCallback.Clear();
		}

		public void Start()
		{
			this.channel.Start();
		}

		public IPEndPoint RemoteAddress
		{
			get
			{
				return this.channel.RemoteAddress;
			}
		}

		public ChannelType ChannelType
		{
			get
			{
				return this.channel.ChannelType;
			}
		}

		public MemoryStream Stream
		{
			get
			{
				return this.channel.Stream;
			}
		}

		public void OnRead(MemoryStream memoryStream)
		{
			try
			{
				this.Run(memoryStream);
			}
			catch (Exception e)
			{
				Log.Error(e);
			}
		}

		private void Run(MemoryStream memoryStream)
		{
            IInMessage message;
			try
			{
                this.Network.MessageHeader.Deserialize(memoryStream);
#if !SERVER
                if (OpcodeHelper.IsClientHotfixMessage(this.Network.MessageHeader.ID))
                {
                    this.GetComponent<SessionCallbackComponent>().MessageCallback.Invoke(this, this.Network.MessageHeader, memoryStream);
                    return;
                }
#endif
                OpcodeTypeComponent opcodeTypeComponent = this.Network.Entity.GetComponent<OpcodeTypeComponent>();
                IInMessage instance = opcodeTypeComponent.GetInputInstance(this.Network.MessageHeader.ID);
                if (instance == null) return;
                if (this.Network.MessageHeader.ErrorCode > 0)
                {
                    message = instance;
                }
                else
                {
                    message = (IInMessage)this.Network.MessagePacker.DeserializeFrom(instance, memoryStream);
                }
                instance.ErrorCode = this.Network.MessageHeader.ErrorCode;


                if (OpcodeHelper.IsNeedDebugLogMessage(this.Network.MessageHeader.ID))
				{
					Log.Msg(message);
				}
			}
			catch (Exception e)
			{
				// 出现任何消息解析异常都要断开Session，防止客户端伪造消息
				Log.Error($"opcode: {(this.Network.MessageHeader.ID & 0xFFFF)} {this.Network.Count} {e}, ip: {this.RemoteAddress}");
				this.Error = ErrorCode.ERR_PacketParserError;
				this.Network.Remove(this.Id);
				return;
			}
			
			Action<IInMessage> action;
			if (!this.requestCallback.TryGetValue(this.Network.MessageHeader.RpcId, out action))
			{
                this.Network.MessageDispatcher.Dispatch(this, this.Network.MessageHeader.ID, message);
            }
            else
            {
                this.requestCallback.Remove(this.Network.MessageHeader.RpcId);
                action(message as IInMessage);
            }

            this.Network.MessageHeader.Clear();

        }

		public ETTask<IInMessage> Call(IOutMessage request)
		{
            RpcId = RpcIdGenerater.GenerateId();
            var tcs = new ETTaskCompletionSource<IInMessage>();

			this.requestCallback[RpcId] = (response) =>
			{
                if (response == null || ErrorCode.IsRpcNeedThrowException(this.Network.MessageHeader.ErrorCode))
                {
                    throw new RpcException(this.Network.MessageHeader.ErrorCode, $"内部错误{this.Network.MessageHeader.ErrorCode}！");
                }

                tcs.SetResult(response);
			};

            this.Network.MessageHeader.RpcId = RpcId;
            this.Send(request);
			return tcs.Task;
		}

		public ETTask<IInMessage> Call(IOutMessage request, CancellationToken cancellationToken)
		{
            RpcId = RpcIdGenerater.GenerateId();
            var tcs = new ETTaskCompletionSource<IInMessage>();

			this.requestCallback[RpcId] = (response) =>
			{
                if (response == null || ErrorCode.IsRpcNeedThrowException(this.Network.MessageHeader.ErrorCode))
                {
                    throw new RpcException(this.Network.MessageHeader.ErrorCode, $"内部错误{this.Network.MessageHeader.ErrorCode}！");
                }

                tcs.SetResult(response);
			};

			cancellationToken.Register(() => this.requestCallback.Remove(RpcId));

            this.Network.MessageHeader.RpcId = RpcId;
            this.Send(request);
			return tcs.Task;
		}

		public void Reply(IOutMessage message)
		{
			if (this.IsDisposed)
			{
				throw new Exception("session已经被Dispose了");
			}

			this.Send(message);
		}

		public void Send(IOutMessage message)
		{
			OpcodeTypeComponent opcodeTypeComponent = this.Network.Entity.GetComponent<OpcodeTypeComponent>();
			int id = opcodeTypeComponent.GetOutputID(message.GetType());
			
			Send(id, message);
		}
		
		public void Send(int id, IOutMessage message)
		{
			if (this.IsDisposed)
			{
				throw new Exception("session已经被Dispose了");
			}
			
			if (OpcodeHelper.IsNeedDebugLogMessage(id) )
			{
#if !SERVER
				if (OpcodeHelper.IsClientHotfixMessage(id))
				{
				}
				else
#endif
				{
					Log.Msg(message);
				}
			}

			MemoryStream stream = this.Stream;			
			stream.Seek(0, SeekOrigin.Begin);
            this.Network.MessageHeader.ID = id;
            this.Network.MessageHeader.Serialize(stream);
            this.Network.MessagePacker.SerializeTo(message, stream);
			stream.Seek(0, SeekOrigin.Begin);

#if SERVER
			// 如果是allserver，内部消息不走网络，直接转给session,方便调试时看到整体堆栈
			if (this.Network.AppType == AppType.AllServer)
			{
				Session session = this.Network.Entity.GetComponent<NetInnerComponent>().Get(this.RemoteAddress);
				session.Run(stream);
				return;
			}
#endif

            this.Send(stream);

            this.Network.MessageHeader.Clear();
        }

		public void Send(MemoryStream stream)
		{
			channel.Send(stream);
		}
	}
}